/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <memory.h>
#include <getopt.h>
#include <pthread.h>
#include <errno.h>
#include <curl/curl.h>
#include <signal.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include "device.h"
#include "utils.h"
#include "win.h"
#include "tuner_ceton.h"
#include "mpegts_io.h"

//#define	CETON_DEFAULT_READ_SZ				(4096 * TS_PACKET_SIZE)
#define	CETON_DEFAULT_READ_SZ				(171 * TS_PACKET_SIZE)
#define	CETON_DEFAULT_MAX_TUNER_COUNT		4
#define CETON_DEFAULT_BINDIP				"0.0.0.0"
#define CETON_DEFAULT_BINDPORT 				8989
#define CETON_DEFAULT_DEVIP 				"192.168.2.20"
#define CETON_DEFAULT_DEVPATH 				"/dev/ctn91xx_mpeg0_"



// Callback(s) for device
int ceton_tune_channel(TUNER_STRUCT *, TUNER_INITPASS_STRUCT*);
uint8_t ceton_read_data(TUNER_STRUCT *);

//void *ceton_read_thread(void *);

// Internal functions, shouldn't be called from outside
int _ceton_channel_change(TUNER_CETON_STRUCT *, TUNER_STRUCT *, int);
int _ceton_get_field_int(char *, int);
char * _ceton_get_field_str(char *, int);
uint8_t _ceton_parse_class_var(uint8_t *, uint32_t);
MPEGTS_IO_PACKET_STRUCT *_ceton_parse_packet(uint8_t *, uint32_t);

CURLcode _ceton_getvar(TUNER_CETON_STRUCT *, int, char *, char *, UTILS_DATAPAIR_STRUCT *);
CURLcode _ceton_get_channel_lineup(TUNER_CETON_STRUCT *);
uint8_t ceton_destroy(TUNER_STRUCT *);
uint8_t ceton_close_data(TUNER_STRUCT *);


/* Ceton Specific for tuning/changing channels */
int _ceton_channel_change(TUNER_CETON_STRUCT * dev, TUNER_STRUCT *tuner, int chan)
{
	int i=0, j=0, try_count=0, k=0;
	CURLcode res;
	char post_fields[128] = {0};
	char chan_url[3072] = {0};
	char error_buf[CURL_ERROR_SIZE] = {0};
	uint8_t c_lock = '0', pcr_lock = '0';
	UTILS_DATAPAIR_STRUCT carg = {0};

	if (!dev->curl_handle)
	{
		curl_global_init(CURL_GLOBAL_ALL);
		dev->curl_handle = curl_easy_init();
	}

	// Channel Change POST
	sprintf(chan_url,"http://%s/channel_request.cgi", dev->device_ip);
	sprintf(post_fields,"instance_id=%d&channel=%d", tuner->id, chan);
//	utils_log_vprint(dev->device, "[%d] ceton_channel_change: %s -- %s\n", chan, chan_url, post_fields);

	pthread_mutex_lock(&dev->curl_mutex);
	curl_easy_reset(dev->curl_handle);
	curl_easy_setopt(dev->curl_handle, CURLOPT_URL, chan_url);
	curl_easy_setopt(dev->curl_handle, CURLOPT_POST, 1);
	curl_easy_setopt(dev->curl_handle, CURLOPT_POSTFIELDS, (void*)post_fields);
	curl_easy_setopt(dev->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
	res = curl_easy_perform(dev->curl_handle);
	pthread_mutex_unlock(&dev->curl_mutex);

	if (res != 0)
	{
		utils_log_print(dev->device->log, "[CETON] Channge change error1 (i: %d, chan: %d): %s\n", tuner->id, chan, error_buf);
		return -1;
	}

	// CarrierLock
	do
	{
		res = _ceton_getvar(dev, tuner->id, "tuner", "CarrierLock", &carg);
		if (res != 0)
		{
			utils_log_print(dev->device->log, "[CETON] Error Quering Carrier Lock (i: %d, chan: %d): %s\n", tuner->id, chan, error_buf);
			return -1;
		}

		c_lock = _ceton_parse_class_var(carg.data, carg.data_size);
		if (c_lock == '0')
		{
			usleep(250*1000);
			if (carg.data)
				free(carg.data);
//			memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
			memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));
		}
	} while (c_lock == '0');

	if (carg.data)
		free(carg.data);
//	memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
	memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));

	//PCR LOck
	do
	{
		res = _ceton_getvar(dev, tuner->id, "tuner", "PCRLock", &carg);
		if (res != 0)
		{
			utils_log_print(dev->device->log, "[CETON] Error Quering PCR Lock (i: %d, chan: %d): %s\n", tuner->id, chan, error_buf);
			return -1;
		}

		c_lock = _ceton_parse_class_var(carg.data, carg.data_size);
		if (c_lock == '0')
		{
			usleep(250*1000);
			if (carg.data)
				free(carg.data);
//			memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
			memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));
		}
	} while (c_lock == '0');

	if (carg.data)
		free(carg.data);
//	memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
	memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));

	// Virtual Channe Query
	sprintf(chan_url,"http://%s/get_var?i=%d&s=cas&v=VirtualChannelNumber' ", dev->device_ip, tuner->id);
//	if (debug_level)
//		utils_log_print(cetondev->device->log, "[CETON] Querying Virtual Channel, URL: %s\n", chan_url);

	pthread_mutex_lock(&dev->curl_mutex);
	curl_easy_reset(dev->curl_handle);
	curl_easy_setopt(dev->curl_handle, CURLOPT_URL, chan_url);
	curl_easy_setopt(dev->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEFUNCTION, generic_curl_handler);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEDATA, (void*)&carg);

	memset(post_fields, 0, 128);

	do
	{
		res = curl_easy_perform(dev->curl_handle);
		if (res != 0)
		{
			utils_log_print(dev->device->log, "[CETON] Error Quering Virtual Channel Number (i: %d, chan: %d): %s\n", tuner->id, chan, error_buf);
			return -1;
		}
		for (i=0; i < carg.data_size - 13; i++)
		{
			if (memcmp("class=\"get\">", &carg.data[i], 12) == 0)
			{
				for (j=0; j < 128; j++)
				{
					if (carg.data[j+i+12] == '<')
						break;
					post_fields[j] = carg.data[j+i+12];
				}
				break;
			}
		}
		i = atoi(post_fields);

		if (i != chan)
		{
			usleep(250 * 1000);

			if (carg.data)
				free(carg.data);
//			memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
			memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));
		}
//		utils_log_print(cetondev->device->log, "Vchannel Value: %d (%d)\n", i, j);
	}
	while(i != chan);

	pthread_mutex_unlock(&dev->curl_mutex);

	if (carg.data)
		free(carg.data);
//	memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
	memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));


	// Copy Flags Query
	sprintf(chan_url,"http://%s/get_var?i=%d&s=diag&v=CopyProtectionStatus", dev->device_ip, tuner->id);
//	if (debug_level)
//		utils_log_print(cetondev->device->log, "[CETON] Querying Copy Flags, URL: %s\n", chan_url);

	pthread_mutex_lock(&dev->curl_mutex);
	curl_easy_reset(dev->curl_handle);
	curl_easy_setopt(dev->curl_handle, CURLOPT_URL, chan_url);
	curl_easy_setopt(dev->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEFUNCTION, generic_curl_handler);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEDATA, (void*)&carg);

	j = 4;
	k = 0;

	do
	{
		if (carg.data)
			free(carg.data);
//		memset(&carg.data, 0, sizeof(UTILS_DATAPAIR_STRUCT));
		memset(&carg, 0, sizeof(UTILS_DATAPAIR_STRUCT));

		res = curl_easy_perform(dev->curl_handle);
		if (res != 0)
		{
			utils_log_print(dev->device->log, "[CETON] Error Quering Copy Flags (i: %d, chan: %d): %s\n", tuner->id, chan, error_buf);
			return -1;
		}
		for (i=0; i < carg.data_size - 13; i++)
		{
			if (memcmp(")</body></html>", &carg.data[i], 15) == 0)
			{
//				utils_log_print(cetondev->device->log, "FOUND! %c%c\n", carg.data[i-2], carg.data[i-1]);
				c_lock = carg.data[i-2];
				pcr_lock = carg.data[i-1];
				break;
			}
			else if (memcmp("None</body></html>", &carg.data[i], 18) == 0)
			{
				k++;
			}
		}

		if (c_lock != '0')
		{
			usleep(500 * 1000);
		}

		if (pcr_lock == '0')
		{
			j = 0;
		}
		else
		{
			usleep(250 * 1000);
			j--;
		}
		try_count++;
		if (try_count > 4)
		{
			if (c_lock == '1')
			{
				c_lock = '0';
				pcr_lock = '0';
			}
			break;
		}
	} while (c_lock != '0' || j > 0);

//	if (debug_level)
//		utils_log_print(cetondev->device->log, "[CETON] Querying Copy Flags DONE\n");
	pthread_mutex_unlock(&dev->curl_mutex);

	if (carg.data)
		free(carg.data);

	if (k < 3 && c_lock == '0' && isdigit(pcr_lock))
	{
		return (pcr_lock - 48);
	}
	if (k>= 3)
		return (pcr_lock - 48);
	return -1;
}

char * _ceton_get_field_str(char *line, int count)
{
	char * ret = NULL;
	int len=strlen(line);
	char *strtmp = calloc(1, len);
	char *str = strdup(line);

	if (str && strtmp)
	{
		int i, j=0, k = 0;
		uint8_t write = 0;

		for(i=0; i <  len-4; i++)
		{
			if (str[i] == '<'  && str[i+1] == '/' && str[i+2] == 't' && str[i+3] == 'r')
				break;

			if (str[i-1] == '>' && str[i] != '<')
			{
				write = 1;
				j = 0;
				memset(strtmp, 0, len);
			}

			if (str[i] == '<' && write)
			{
				k++;
				if (k == count)
				{
					//ret = strtol(strtmp, (char **)NULL, 10);
					ret = strtmp;
					break;
				}
				write = 0;
			}

			if (write)
				strtmp[j++] = str[i];
		}

		free(str);
//		free(strtmp);
	}
	return ret;
}

int _ceton_get_field_int(char *line, int count)
{
	int ret = -1;
	char *tmp = _ceton_get_field_str(line, count);
	if (tmp)
	{
		ret = strtol(tmp, (char **)NULL, 10);
		free(tmp);
	}
	return ret;
}

CURLcode _ceton_get_channel_lineup(TUNER_CETON_STRUCT * dev)
{
	char chan_url[1024] = {0};
	char error_buf[CURL_ERROR_SIZE] = {0};
	CURLcode res;
	UTILS_DATAPAIR_STRUCT carg = {0};

	if (!dev->curl_handle)
	{
		curl_global_init(CURL_GLOBAL_ALL);
		dev->curl_handle = curl_easy_init();
	}
	while (!dev->device)
	{
		utils_log_print(dev->device->log, "[CETON] Waiting for channel list\n");
//		utils_log_print(cetondev->device->log, "NOOOOOOOOO\n");
		usleep(1000*1000);
	}
	// Virtual Channe Query
	sprintf(chan_url,"http://%s/view_channel_map.cgi?page=0", dev->device_ip);
//	utils_log_print(cetondev->device->log, "_ceton_get_channel_lineup, 1: %s\n", chan_url);

	pthread_mutex_lock(&dev->curl_mutex);
	curl_easy_reset(dev->curl_handle);
	curl_easy_setopt(dev->curl_handle, CURLOPT_URL, chan_url);
	curl_easy_setopt(dev->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEFUNCTION, generic_curl_handler);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEDATA, (void*)&carg);
	res = curl_easy_perform(dev->curl_handle);

	if (carg.data)
	{
		TEXTFILE_IN_MEMORY *m = memfile_init((char*)carg.data, carg.data_size);
		if (m)
		{
			int i=0;;
			char *tmpline = NULL;

			do
			{
				tmpline = memfile_getnext_line(m, 0);

				if (tmpline)
				{
					i++;
					if (memcmp("<tr><td>", tmpline, 8) == 0)
					{
						int q;
						int vchannel = -1, pgm_num = -1;
						char *moo = NULL;

						vchannel = _ceton_get_field_int(tmpline + 4, 1);
						pgm_num = _ceton_get_field_int(tmpline, 5);
						moo = _ceton_get_field_str(tmpline, 2);
						device_channel_add(dev->device, vchannel, pgm_num);

						for(q=0; q < strlen(moo); q++)
						{
							if (moo[q] == ' ')
								moo[q] = '\0';
						}
						device_channel_add_name(dev->device, vchannel, pgm_num, moo);
						free(moo);
					}
				}
			} while(tmpline != NULL);
			free(m);
		}

		free(carg.data);
	}
	pthread_mutex_unlock(&dev->curl_mutex);

	utils_log_print(dev->device->log, "[CETON] Channel count: %d (url: %s)\n", dev->device->channel_count, chan_url);

	return res;

}

CURLcode _ceton_getvar(TUNER_CETON_STRUCT * dev, int devnum, char *qtype, char *varname, UTILS_DATAPAIR_STRUCT *carg)
{
	char chan_url[1024] = {0};
	char error_buf[CURL_ERROR_SIZE] = {0};
	CURLcode res;
	if (!dev->curl_handle)
	{
		curl_global_init(CURL_GLOBAL_ALL);
		dev->curl_handle = curl_easy_init();
	}

	// Virtual Channe Query
	sprintf(chan_url,"http://%s/get_var?i=%d&s=%s&v=%s' ", dev->device_ip, devnum, qtype, varname);

//	if (debug_level)
//		utils_log_print(cetondev->device->log, "[CETON] Querying URL: %s\n", chan_url);

	pthread_mutex_lock(&dev->curl_mutex);
	curl_easy_reset(dev->curl_handle);
	curl_easy_setopt(dev->curl_handle, CURLOPT_URL, chan_url);
	curl_easy_setopt(dev->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEFUNCTION, generic_curl_handler);
	curl_easy_setopt(dev->curl_handle, CURLOPT_WRITEDATA, (void*)carg);
	res = curl_easy_perform(dev->curl_handle);
	pthread_mutex_unlock(&dev->curl_mutex);
	return res;
}


uint8_t _ceton_parse_class_var(uint8_t *data, uint32_t data_len)
{
	char ret = '0';

	if (data && data_len)
	{
		int i;

		for (i=0; i < data_len - 13; i++)
		{
			if (memcmp("class=\"get\">", &data[i], 12) == 0)
			{
				ret = data[i+12];
				break;
			}
		}
	}
	return ret;
}

/* End of Ceton Specific Stuff */
uint8_t ceton_read_data(TUNER_STRUCT * tuner)
{
	uint8_t ret = 1;
	DEVICE_STRUCT *device = tuner->parent;
	TUNER_CETON_STRUCT * cdev = device->parent;
	if (tuner && tuner->read_data_ptr)
	{
//		int err = 0;
		size_t bytes_read = 0, offset = 0;
		size_t read_sz = tuner->read_size;
		ret = 0;

//		utils_log_print(cetondev->device->log, "HI1, sock: %d, sz: %" PRId64"\n", tuner->devhandle, read_sz);
		do
		{
			if (offset == 0)
			{
//				bytes_read = get_bytes_from_file(tuner->devhandle, tuner->read_data_ptr, 1);
				bytes_read = get_bytes_from_file(tuner->devhandle, tuner->read_data_ptr, 1);
				if (!(bytes_read == 1 && tuner->read_data_ptr[0] == TS_SYNC_BYTE))
				{
//					TUNER_CETON_STRUCT *ceton_device = device->parent;
//					_tuner_printw(ceton_device, "Need sync..\n");
					usleep(1000*500);
					continue;
				}
			}
			else
			{
				bytes_read = get_bytes_from_file(tuner->devhandle, tuner->read_data_ptr + offset, read_sz);
			}

//			bytes_read = get_bytes_from_socket(tuner->devhandle, tuner->read_data_ptr + offset, read_sz, 2, 0, tuner->read_block_time);
//			bytes_read = read(tuner->devhandle, tuner->read_data_ptr + offset, read_sz);

//			utils_log_print(cetondev->device->log, "[%s %d] R: %" PRId64"\n", tuner->name, tuner->id,bytes_read);
			if ((int64_t)bytes_read <= -1)
			{
				utils_log_print(cdev->device->log, "[CETON] ceton_read_data, get_bytes err: %s (%d) (%" PRId64")\n",  strerror(errno), errno, (int64_t)bytes_read);
				ret = 1;
				break;
			}
			else if (bytes_read == 0)
			{
				usleep(tuner->read_thread_sleep_time);
			}

			read_sz -= bytes_read;
			offset += bytes_read;
		}
		while (offset < tuner->read_size);
	}
	else
	{
		utils_log_print(cdev->device->log, "[CETON] ceton_read_data error, something null\n");
	}
	return ret;
}

//int ceton_tune_channel(TUNER_STRUCT * tuner, uint32_t chan)
int ceton_tune_channel(TUNER_STRUCT * tuner, TUNER_INITPASS_STRUCT *tpass)
{
	int chanch = -1;

//	utils_log_print(cetondev->device->log, "ceton_tune_channel -- start: %d\n", chan);
//	printf( "ceton_tune_channel -- start: %d\n", chan);
	if (tuner && tuner->parent && tpass)
	{
		uint32_t chan = tpass->channel_num;
		DEVICE_STRUCT *dev = (DEVICE_STRUCT *)tuner->parent;
		TUNER_CETON_STRUCT * cetondev = (TUNER_CETON_STRUCT *)dev->parent;


//		if (tuner->devhandle > 2)

		chanch = _ceton_channel_change(cetondev, tuner, (int)chan);
		if (chanch == 0)
		{
			uint16_t pid = 0;
//			uint8_t start = 0;
			uint32_t packet_count = 0;
			uint8_t flush[188] = {0}, need_pat = 1;
			int b = 0, c = 0;
			int count = 0, sync_count = 0;

			if (tuner->devhandle < 3)
				tuner->devhandle = open(tuner->name, O_RDONLY);

			if (tuner->devhandle < 2)
			{
				return 1;
			}

			while(b < TS_PACKET_SIZE * 10 || need_pat)
			{
				c = get_bytes_from_file(tuner->devhandle, flush, 1);
				if (c == 1 && flush[0] == 0x47)
				{
					c = get_bytes_from_file(tuner->devhandle, (uint8_t*)&flush[1], TS_PACKET_SIZE - 1);
					if (c <= 0)
					{
						utils_log_print(cetondev->device->log, "Wait... %d (count: %d) -- %s %d\n", c, count,  strerror(errno), errno);
						count++;
						usleep(250*1000);
						if (count > 8)
						{
							utils_log_print(cetondev->device->log, "[CETON] No data from handle, exiting\n");
							return 1;
						}
						continue;
					}

					pid = ((flush[1] & 0x1F) << 8) | flush[2];
//					start = (flush[1] & 0x40) >> 6;
					b += (c+1);
					packet_count++;

					if (pid == 0)
						need_pat = 0;

					if (packet_count > 25000)
					{
						utils_log_print(cetondev->device->log, "[CETON] No PAT, exiting\n");
						return 1;
					}
				}
				else
				{
					if (c == 1)
					{
						sync_count++;
					}
					else
					{
//						utils_log_print(cetondev->device->log, "Wait1... %d (count: %d) -- %s %d\n", c, count,  strerror(errno), errno);
						usleep(250*1000);
					}
				}

				if (sync_count > TS_PACKET_SIZE * 3)
				{
					utils_log_print(cetondev->device->log, "[CETON] Couldn't sync stream, exiting\n");
					return 1;
				}
			}

			utils_log_print(cetondev->device->log, "[CETON] Setting tuned channel: %d [dev number: %d, name: %s, pat sync count: %d, ts_sync: %d]\n", chan, tuner->id, tuner->name, packet_count, sync_count);
			tuner->tuned_channel = chan;
			return 0;
		}
		else
		{
			utils_log_print(cetondev->device->log, "[CETON] Problem tuning channel: %d [TID: %d]\n", chanch, tuner->thread_id);
			return chanch;
		}
	}
//	utils_log_print(cetondev->device->log, "ceton_tune_channel -- end\n");
	return 1;
}

uint8_t ceton_close_data(TUNER_STRUCT * tuner)
{
	uint8_t ret = 1;
	if (tuner)
	{
	    DEVICE_STRUCT *device = tuner->parent;
	    TUNER_CETON_STRUCT * cdev = device->parent;
		if(tuner->devhandle > 2)
		{
			utils_log_print(cdev->device->log, "[CETON] closing socket: %d, New: ", tuner->devhandle);
//			utils_log_print(cetondev->device->log, "[CETON] closing socket: %d, New: ", fileno(tuner->filehandle));
//			fclose(tuner->filehandle);
//			tuner->filehandle = 0;

			close(tuner->devhandle);
			tuner->devhandle = 0;

			usleep(1000* 250);
//			tuner->filehandle = fopen(tuner->name, "rb");
//			tuner->devhandle = open(tuner->name, O_RDONLY);
//			utils_log_print(cetondev->device->log, "%d\n",  fileno(tuner->filehandle));
			utils_log_print(cdev->device->log, "%d\n",  tuner->devhandle);
		}
	}
	return ret;
}

uint8_t ceton_destroy(TUNER_STRUCT * tuner)
{
	TUNER_STRUCT * del = NULL;
	DEVICE_STRUCT *device = tuner->parent;
	TUNER_CETON_STRUCT * cdev = device->parent;

	utils_log_print(cdev->device->log, "\n[CETON] Exiting, cleaning up...\n");
	while(tuner)
	{
		device_tuner_destroy(tuner);
		del = tuner;
		tuner = tuner->next;

		if (del)
		{
			mpegts_io_close(del->mpegts_io, 1);
			//free(del);
		}

	}
	device_destroy(device);
	return 0;
}


//TUNER_CETON_STRUCT * tuner_ceton_init(char *ceton_ip, char * ceton_path, int max_tuners, char *bindip, uint16_t bindport)
TUNER_CETON_STRUCT * tuner_ceton_init(DEVICE_INIT_ARGS_STRUCT *devopts)
{
	TUNER_CETON_STRUCT * cetondev = NULL;

	if (devopts && devopts->bind_ip && devopts->bind_port && devopts->device_path && devopts->max_tuners > 0)
	{
		cetondev = calloc(1, sizeof(TUNER_CETON_STRUCT));
		if (cetondev)
		{

			cetondev->device_ip = strdup(devopts->device_name);

			cetondev->device_path = strdup(devopts->device_path);
			cetondev->device = device_init(devopts->bind_ip, devopts->bind_port);

			utils_log_print(cetondev->device->log, "[CETON] Device and Tuner init (max: %d):\n", devopts->max_tuners);
			utils_log_print(cetondev->device->log, "[CETON]   Ceton IP [Path]: %s [%s]\n", devopts->device_name, devopts->device_path);
//			utils_log_print(cetondev->device->log, "[CETON]   Listen IP:PORT: %s:%d\n", devopts->bind_ip, devopts->bind_port);

			_ceton_get_channel_lineup(cetondev);

			if (cetondev->device)
			{
				TUNER_STRUCT *tmptuner = NULL;
				char tmpdev[MAX_DEVNAME_SIZE] = {0};
				int i, tmp = devopts->max_tuners;
				pthread_mutex_lock(&cetondev->device->tunerlock);
				cetondev->device->parent = (void*) cetondev;
				cetondev->device->stream_only = 1;
				cetondev->device->cb_dev_printf = generic_vprintw;
				for(i=0; i < tmp; i++)
				{
					tmptuner = device_tuner_init(TUNE_TYPE_CETON);

					if (tmptuner)
					{
//						tmptuner->devhandle_is_file = 1;
						sprintf(tmpdev, "%s%d", cetondev->device_path, i);

//						tmptuner->filehandle = fopen(tmpdev, "rb");
						tmptuner->devhandle = open(tmpdev, O_RDONLY);

//						if (tmptuner->filehandle != NULL)
						if (tmptuner->devhandle > 2)
						{
//							utils_log_print(cetondev->device->log, "adding tuner: %d\n", i);
//							tmptuner->packets_to_buffer = 200;
							tmptuner->mpegts_fileunit_maxsize = devopts->max_segment_size;
							tmptuner->mpegts_fileunit_maxlife = devopts->max_segment_life;

							tmptuner->segment_dir = devopts->segment_dir;
							tmptuner->base_filename = devopts->base_filename;

							strncpy(tmptuner->name, tmpdev, strlen(tmpdev));
							tmptuner->read_thread_sleep_time = 1000;
							tmptuner->read_block_time = 128 * 1000;
							tmptuner->cb_read_data = ceton_read_data;
							tmptuner->cb_tune_channel = ceton_tune_channel;
							tmptuner->cb_close_data = ceton_close_data;
							tmptuner->packets_to_buffer = 10;

							tmptuner->read_size = CETON_DEFAULT_READ_SZ;
							tmptuner->parent = (void*)cetondev->device;
							tmptuner->id = i;
							close(tmptuner->devhandle);
							tmptuner->devhandle = 0;

							tmptuner->cb_gui_tuner_read_status = win_tuner_read_status;
							tmptuner->cb_gui_tuner_write_status  = win_tuner_write_status;

							device_tuner_add(cetondev->device, tmptuner);
							pthread_mutex_unlock(&tmptuner->init_mutex);
//							pthread_create(&tmptuner->read_thread, NULL, device_read_thread, (void*)tmptuner);
						}
						else
						{
							devopts->max_tuners--;
							free(tmptuner);
//							utils_log_print(cetondev->device->log, "BAAAAAAAAA1\n");
						}
					}
				}

				cetondev->device->max_tuners = devopts->max_tuners;
				pthread_mutex_unlock(&cetondev->device->tunerlock);

			}
		}
	}
	return cetondev;
}

#ifdef _MAIN_

#define CETON_MAIN_WIN_TITLE "[Main Output -- Ver 1] "

uint8_t sigint_h = 0;
uint8_t	use_curses = 1;

TUNER_CETON_STRUCT * ceton_device = NULL;

DEVICE_INIT_ARGS_STRUCT _opts = {	.bind_ip = CETON_DEFAULT_BINDIP,
									.bind_port = CETON_DEFAULT_BINDPORT,
									.max_segment_size = MPEGTS_IO_MAX_SIZE_DEFAULT,
									.device_name = CETON_DEFAULT_DEVIP,
									.device_path = CETON_DEFAULT_DEVPATH,
									.max_tuners = CETON_DEFAULT_MAX_TUNER_COUNT

								};
void display_usage(char *exe)
{
//	fprintf(stderr,"bleah\n");
}


static const char *optString = "1:2:s:f:l:p:m:a:d:n?h";

static const struct option longOpts[] =
{
/*
	{ "listenip", required_argument, NULL, 'l' },
	{ "listenport", required_argument, NULL, 'p' },
	{ "tuners", required_argument, NULL, 'm' },
	{ "cetonip", required_argument, NULL, 'c' },
	{ "cetonpath", required_argument, NULL, 'd' },
*/
	{ "help", no_argument, NULL, 'h' },
	{ NULL, no_argument, NULL, 0 }
};

uint8_t get_opts(int argc, char *const argv[], DEVICE_INIT_ARGS_STRUCT * devopts)
{
	int index = 0;
	int opt = getopt_long( argc, argv, optString, longOpts, &index );

	if (opt == -1 )
	{
		display_usage(argv[0]);
		return 1;
	}


	while ( opt != -1 )
	{
		switch ( opt )
		{
			case 'n':
				use_curses = !use_curses;
				break;
			case '1':
				devopts->max_segment_size = atoi(optarg);
				break;

			case '2':
				devopts->max_segment_life = atoi(optarg);
				break;

			case 'l':
				devopts->bind_ip = optarg;
				break;

			case 'p':
				devopts->bind_port = atoi(optarg);
				break;

			case 'm':
				devopts->max_tuners = atoi(optarg);
				break;

			case 'a':
				devopts->device_name = optarg;
				break;

			case 'd':
				devopts->device_path = optarg;
				break;

			case 's':
				devopts->segment_dir = optarg;
				break;

			case 'f':
				devopts->base_filename = optarg;
				break;

			case 'h':
			case '?':
				display_usage(argv[0]);
				break;

			default:
				break;
		}
		opt = getopt_long( argc, argv, optString, longOpts, &index );
	}
	return 0;
}

void sigint_handler()
{
	if (!sigint_h)
	{
		if (!use_curses)
		{
			pthread_mutex_unlock(&ceton_device->init_mutex);
		}
		sigint_h = 1;
	}
}

void sigpipe_handler()
{
//   utils_log_print(cetondev->device->log, "SIGPIPE caught\n");
}

int main(int argc, char *argv[])
{
	int ch;
	signal(SIGPIPE,sigpipe_handler);
	signal(SIGINT,sigint_handler);

	get_opts(argc, argv, &_opts);

	_opts.max_segment_size = get_nearest_page_size(_opts.max_segment_size, TS_PACKET_SIZE);
//printf("MOO1\n");
	ceton_device = tuner_ceton_init(&_opts);
//printf("MOO2\n");

	if (ceton_device)
	{
		if (use_curses)
		{
			int loop =1;

			ceton_device->screen = edpvr_win_init(ceton_device->device);

			while (loop && !sigint_h)
			{
				ch = getch();
				switch (ch)
				{
					case 'q' :
					case 'Q' :
						loop = 0;
					break;

					case ERR:
					{
						char *msg = NULL;
						do
						{
							msg = utils_log_getmsg(ceton_device->device->log);
							if (msg)
							{
								win_screen_printw(ceton_device->screen, msg);
								free(msg);
							}
						} while (msg);
						usleep(1000 * 250);
						win_screen_update_all(ceton_device->screen);
					}
					break;

					default:
					break;
				}
			}
		}
		else
		{
			char *msg = NULL;
			while(!sigint_h)
			{
				do
				{
					msg = utils_log_getmsg(ceton_device->device->log);
					if (msg)
					{
//						win_screen_printw(restream_device->screen, msg);
						printf("%s", msg);
						free(msg);
					}
				} while (msg);
				usleep(1000);
			}
		}

		if (ceton_device && ceton_device->device && ceton_device->device->first_tuner)
			ceton_destroy(ceton_device->device->first_tuner);
		if (use_curses)
			win_destroy_screen(ceton_device->screen);

	}
	printf("Exiting Ceton app....\n");

	return 0;
}
#endif
